﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;

namespace SMARTMobile.WinPhone
{
    public class DataSourceService : IApplicationService
    {
        private static readonly IDictionary<string, object> Cache = new Dictionary<string, object>();
        private DataSourcesConfiguration _configuration;
        
        public static DataSourceService Current { get; private set; }

        public void StartService(ApplicationServiceContext context)
        {
            Debug.WriteLine("DataSourceService::StartService()");
            Debug.Assert(null == Current);
            Current = this;
            _configuration = PhoneConfiguration.Current.DataSourceMap;
        }

        public void StopService()
        {
            Debug.WriteLine("DataSourceService::StopService()");
            DataSourceRequest.DisposeSubscriptions();
        }

        public DataSource this[string sourcename]
        {
            get
            {
                return _configuration.FindSource(sourcename);
            }
        }

        public Guid GetImage(Uri uri, IObserver<Stream> observer)
        {
            try
            {
                DataSourceRequest request = DataSourceRequest.Create(uri);
                return request.SubscribeImage(observer);
            }
            catch (Exception e)
            {
                DebugTrace(e.ToString());
#if DEBUG
                throw;
#endif
            }

            return Guid.Empty;
        }

        public DataSourceRequest CreateRequest(string name)
        {
            return CreateRequest(name, true);
        }

        public DataSourceRequest CreateRequest(string name, bool observeOnDispatcher)
        {
            DataSource source = _configuration.FindSource(name);
            Debug.Assert(null != source && null != source.Endpoint);
            return DataSourceRequest.Create(source, observeOnDispatcher);
        }

        public IDictionary<string, string> ReadDictionary(string name)
        {
            if (!Cache.ContainsKey(name))
            {
                DataSource source = _configuration.FindSource(name);
                Debug.Assert(null != source);

                switch (source.Type)
                {
                    case DataSourceType.Text:
                        IDictionary<string, string> dictionary = new Dictionary<string, string>();
                        foreach (Parameter paramater in source.Paramaters)
                        {
                            dictionary.Add(paramater.Name, paramater.Value);
                        }
                        Cache.Add(name, dictionary);
                        break;
                    default:
                        return null;

                }
            }
            return Cache[name] as IDictionary<string, string>;
        }

        public ObservableCollection<string> Read(string name)
        {
            if (!Cache.ContainsKey(name))
            {
                DataSource source = _configuration.FindSource(name);
                Debug.Assert(null != source);

                switch (source.Type)
                {
                    case DataSourceType.Text:
                        var query = from p in source.Paramaters select p.Value;
                        Cache.Add(name, new ObservableCollection<string>(query));
                        break;
                    default:
                        return null;

                }
            }
            return Cache[name] as ObservableCollection<string>;
        }

        public T Read<T>(string name) where T : class
        {
            DataSource source = _configuration.FindSource(name);
            Debug.Assert(null != source);

            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists(source.Name))
                {
                    return default(T);
                }
                string pathname = Path.Combine(source.Name, string.Format("{0}.bin", source.Name));
                if (!store.FileExists(pathname)) return default(T);

                using (IsolatedStorageFileStream stream = store.OpenFile(pathname, FileMode.Open, FileAccess.Read))
                {
                    if (0 == stream.Length) // file is corrupt
                    {
                        stream.Close();
                        store.DeleteFile(pathname);
                        return default(T);
                    }

                    try
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                        return serializer.ReadObject(stream) as T;
                    }
                    catch (Exception e)
                    {
                        // File is corrupt
                        Debug.WriteLine(e.ToString());
                        stream.Close();
                        store.DeleteFile(pathname);
                        return default(T);
                    }
                }
            }
        }

        public T Read<T>(string folder, string name) where T : class
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists(folder))
                {
                    return default(T);
                }

                string filePath = Path.Combine(folder, string.Format("{0}.bin", name));
                if (!store.FileExists(filePath))
                    return default(T);

                using (IsolatedStorageFileStream stream = store.OpenFile(filePath, FileMode.Open, FileAccess.Read))
                {
                    if (0 == stream.Length) // file is corrupt
                    {
                        stream.Close();
                        store.DeleteFile(filePath);
                        return default(T);
                    }

                    try
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                        var list = serializer.ReadObject(stream) as T;
                        return list;
                    }
                    catch (Exception e)
                    {
                        // File is corrupt
                        Debug.WriteLine(e.ToString());
                        stream.Close();
                        store.DeleteFile(filePath);
                        return default(T);
                    }
                }
            }
        }

        public void Write<T>(string name, T postdata)
        {
            DataSource source = _configuration.FindSource(name);
            Debug.Assert(null != source);
            switch (source.Type)
            {
                case DataSourceType.IsolatedStorage:
                    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        if (!store.DirectoryExists(source.Name))
                        {
                            store.CreateDirectory(source.Name);
                        }
                        string pathname = Path.Combine(source.Name, string.Format("{0}.bin", source.Name));
                        using (IsolatedStorageFileStream stream = store.OpenFile(pathname, FileMode.Create, FileAccess.Write))
                        {
                            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                            serializer.WriteObject(stream, postdata);
                            stream.Flush(true);
                        }
                    }

                    break;
            }
        }

        public void Write<T>(string folder, string name, T data)
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists(folder))
                {
                    store.CreateDirectory(folder);
                }

                string filePath = Path.Combine(folder, string.Format("{0}.bin", name));
                using (IsolatedStorageFileStream writerStream = store.OpenFile(filePath, FileMode.Create, FileAccess.Write))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                    serializer.WriteObject(writerStream, data);
                    writerStream.Flush(true);
                }
            }
        }

        public Uri GetEndpointUri(string endpointName)
        {
            return _configuration.FindEndpoint(endpointName);
        }

        [Conditional("DEBUG")]
        protected void TraceEntrance()
        {

            var stackTrace = new StackTrace();
            var type = GetType();
            string message = string.Format("{0}::{1} : --> {2}", type.Name, stackTrace.GetFrame(1).GetMethod().Name, DateTime.Now.ToString("HH:mm:ss:ffff"));
            Debug.WriteLine(message);
        }

        [Conditional("DEBUG")]
        protected void TraceExit()
        {

            string message = string.Format("<-- {0}", DateTime.Now.ToString("HH:mm:ss:ffff"));
            var stackTrace = new StackTrace();
            var type = GetType();
            message = string.Format("{0}::{1} : {2}", type.Name, stackTrace.GetFrame(1).GetMethod().Name, message);
            Debug.WriteLine(message);
        }

        [Conditional("DEBUG")]
        protected void DebugTrace(string format, params object[] args)
        {

            string message = string.Format(format, args);
            var stackTrace = new StackTrace();
            var type = GetType();
            message = string.Format("{0}::{1} : {2}", type.Name, stackTrace.GetFrame(1).GetMethod().Name, message);
            Debug.WriteLine(message);
        }

        [Conditional("DEBUG")]
        protected void DebugTrace(string message)
        {
            var stackTrace = new StackTrace();
            var type = GetType();
            message = string.Format("{0}::{1} : {2}", type.Name, stackTrace.GetFrame(1).GetMethod().Name, message);
            Debug.WriteLine(message);
        }
    }
}
